/****************************************************************
 * 
 * Team Cloud 						Intelligent Thermometer
 * 
 * **************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <time.h>

#include "itrs_def.h"
#include "MainLogic.h"

// Threads 
//    1  =  Command line server waiting for instructions
//    2  =  Main thermostat loop regulating the temp
//
int TID[ROOMS] = {1555}; //  ,1556,1557,1558};
pthread_mutex_t mutex;
pthread_cond_t  threshold_cv;

// These are the variables for the threads in which 
// they communicate between and share data
mainLogic logic; //[ROOMS];
FILE *f;
bool server_running; 
char srvrTime[64];


////////////////////////////////////
//// SERVER THREAD /////////////////
////////////////////////////////////
//  This is the function for the main server loop
//  this thread will be waiting for a client command
//
//  *idp is the identifier for which thermostat it 
//       will be commanding 
//  
void *server(void *idp){
	
	server_running = true; // tell other thread server is running
	int hSocket, hServerSocket, nHostPort;  // Socket information
	//struct hostent* pHostInfo;
	struct sockaddr_in Address;
	int nAddressSize=sizeof(struct sockaddr_in);
	bool b; //, loggedin = false;
	char buff[BUFFER_SIZE], cmd[16], arg1[16], arg2[16]; // used for client communication
	float temp1, temp2;          // upper and lower temps 
	//int *room_id = (int *)idp;   // room identifier
	
	//nHostPort = *room_id;    // uncomment this for multiple instances 
	nHostPort = SOCKET;
	
//// server started
	hServerSocket = socket(AF_INET,SOCK_STREAM,0);
	if (hServerSocket == SOCKET_ERROR){
		printf("\nCould not make a SOCKET\n");
		server_running = false; 
		return 0;
	}
/// fill address struct 	    
	Address.sin_addr.s_addr= INADDR_ANY; //0x7F0C0C01; // 127.12.12.1
	Address.sin_port=htons(nHostPort);
	Address.sin_family=AF_INET;
		
//// bind to a port 
    if(bind(hServerSocket,(struct sockaddr*)&Address,sizeof(Address)) == SOCKET_ERROR) {
       printf("\nCould not connect to host\n");
       server_running = false;
       return 0;
    }
//// get port number 
    getsockname( hServerSocket, (struct sockaddr *) &Address,(socklen_t *)&nAddressSize);
    printf("opened socket as fd (%d) on port (%d) for stream i/o\n",hServerSocket, ntohs(Address.sin_port) );
    printf("Making a listen queue of %d elements\n",QUEUE_SIZE);
//// establish listen queue 
    if(listen(hServerSocket,QUEUE_SIZE) == SOCKET_ERROR) {
        printf("\nCould not listen\n");
        server_running = false;
        return 0;
    }
	
	sleep(10);  //wait for the thermostat to 
    
for(;;)  // KEEP THE SERVER RUNNING
{
	sprintf(buff, " ");  // Clear the buffer
	
	// get the connected socket 
    hSocket=accept(hServerSocket,(struct sockaddr*)&Address,(socklen_t *)&nAddressSize);
       
    write(hSocket,buff,BUFFER_SIZE);
    // read from socket into buffer 
    read(hSocket,buff,BUFFER_SIZE);
                
    sscanf(buff,"%s %s %s",cmd,arg1,arg2);

    if( strcmp(cmd, "temp") == 0 ){
    /* Get the current temp from the system
	 * currently using a pseudotemp function.
	 * 
	 */
	   	sprintf(buff, "%3.1f", logic.GetTemp() );
	}
	else if ( strcmp(cmd, "time") == 0 ){
	/*  Get the time from the system
	 *  uses system time.
	 */
		logic.GetTime(srvrTime);
	  	sprintf(buff, "%s", srvrTime );
	}
	else if ( strcmp(cmd, "target") == 0 ){
	/*  Get the target temp
	 */
	 	sprintf(buff, "%3.1f", logic.GetTarget());
	}
	else if ( strcmp(cmd, "upper") == 0 ){
	/*  Get the upper bounds of the temp
	 *  range.
	 */
	 	logic.GetRange(&temp1, &temp2);
	  	sprintf(buff, "%3.1f", temp2 );
	}
	else if ( strcmp(cmd, "lower") == 0 ){
	/*  Get the lower bounds of the temp
	 *  range.
	 */
	 	logic.GetRange(&temp1, &temp2);
	  	sprintf(buff, "%3.1f", temp1 );
	}
	else if ( strcmp(cmd, "settemp") == 0){
	/*  Set the target temp 
	 * 
	 */
	   	temp1 = atof(arg1);
	   	b =  logic.SetTemp(temp1);
	  	if( b )
	   		sprintf(buff,"%3.1f", logic.GetTarget() );
	   	else
	   		sprintf(buff,"temp not set");	    	    
	}
	else if ( strcmp(cmd,"heat") == 0){
	/*
	 * This command will display the heat status
	 * if you supply it with arg1 of on/off you 
	 * can toggle the status of the heat  
	 */
		b = logic.heatStatus();
		if (strcmp(arg1,"off") == 0 ){
			if (b)
				logic.heatToggleStatus();
			sprintf(buff,"OFF");
		}
		else if (strcmp(arg1,"on") == 0 ){
			if (!b)
				logic.heatToggleStatus();
			sprintf(buff,"ON");
		}
		else{
			if (b)
				sprintf(buff,"ON");
			else
				sprintf(buff,"OFF");	
		}
	}
	else if ( strcmp(cmd,"cool") == 0){
	/*
	 * This command will display the cool status
	 * if you supply it with arg1 of on/off you 
	 * can toggle the status of the cool  
	 */
		b = logic.coolStatus();
		if (strcmp(arg1,"off") == 0 ){
			if (b)
				logic.coolToggleStatus();
			sprintf(buff,"OFF");
		}
		else if (strcmp(arg1,"on") == 0 ){
			if (!b)
				logic.coolToggleStatus();
			sprintf(buff,"ON");
		}
		else{
			if (b)
				sprintf(buff,"ON");
			else
				sprintf(buff,"OFF");	
		}
	}
	else if ( strcmp(cmd,"fan") == 0 ){
		b = logic.fanStatus();
		if (strcmp(arg1,"auto") == 0 ){
			if (b)
				logic.fanToggleStatus();
			sprintf(buff,"AUTO");
		}
		else if (strcmp(arg1,"on") == 0 ){
			if (!b)
				logic.fanToggleStatus();
			sprintf(buff,"ON");
		}
		else{
			if (b)
				sprintf(buff,"ON");
			else
				sprintf(buff,"AUTO");	
		}
		
	}
	else if ( strcmp(cmd,"stop") == 0){
	/*  Stop the server loop   */
	   	sprintf(buff,"\nStopping Server");
	   	server_running = false;
	}
	else{
	  	sprintf(buff,"\nCommand not recognized");
	}
	write(hSocket,buff,BUFFER_SIZE);

//	INSERT INTO Temperature (TempID,IndoorTemp,OutdoorTemp,Mode,Fan,DayNight,Cool,Heat,ActiveStage,LastUpdate)
//	 VALUES (NULL,'75','60','AUTO','AUTO','DAY','75','85','COOL','4/56/2008 11:17:15 AM') WHERE LoginID = '1';

//	logic.GetRange(&temp1,&temp2);
//	logic.GetTime(srvrTime);
	
//	sprintf(buff,"%s \"INSERT INTO Temperature "
//			"(TempID,IndoorTemp,Mode,Fan,DayNight,Cool,Heat,LastUpdate)"
//			" VALUES (NULL,'%4.1f','MODE','FAN','DAY','%4.1f','%4.1f','%s')\";",
//			SQLITE, logic.GetTemp(),temp1,temp2,srvrTime);
	
//	printf("%s",buff);
//	system(buff);
	        
	// close socket 
	if(close(hSocket) == SOCKET_ERROR) {
		printf("\nCould not close socket\n");
		server_running = false;
		return 0;
	}
 
	if( ! server_running )
		break;
	
}  ///// MAIN LOOP END /////	  

	pthread_exit(NULL); 
	
}

/////////////////////////////////////
//// THERMOSTAT THREAD /////////////
////////////////////////////////////
//  This thread will maintain the temperature
//  It is called from main and runns a continual loop
//  
//  *idp is passed from main to identify the thermostat
//       if this system is expanded to multiple rooms
//       the idp will be the rooms identifier. 
//
void *Thermostat(void *idp){

	//int *room = (int *)idp;
	//// Start the main logic sensor 	
	logic.initSensor();
	float lower, upper, current_temp;
	char curTime[64], query[256], replyline[64];
	FILE *reply;
	time_t t; 
	struct tm * tmStruct; 
	
	for(;;){
		// Get current time and test against the database to see if
		// the user set a temperature change
        logic.GetTime(curTime);
       
        t = time(NULL);
        tmStruct = localtime(&t);
        if((tmStruct->tm_min%5) == 0){  // check for new temp every 5 minutes in the Database
        	sprintf(query,"%s \"SELECT IndoorTemp FROM temperature WHERE LastUpdate = '%s'\"; ",SQLITE, curTime );
        	reply = popen(query, "r");
        
        	if (reply != NULL ){
        		fgets(replyline, 64, reply);
        		logic.SetTemp(atof(replyline));
        		pclose(reply);
        	}
        }
        
		current_temp = logic.GetTemp();		
		logic.GetRange(&upper,&lower);
		
		//printf("%4.1f, %4.1f ,%4.1f \n", current_temp, lower ,upper);
		if ( (current_temp > upper ) && (logic.coolStatus() == true) ) {
			fprintf(f,"%4.1f|%4.1f|%d|%d|%s",current_temp,logic.GetTarget(),logic.heatStatus(),logic.coolStatus(),curTime);
		}
		else if ((current_temp < lower ) && (logic.heatStatus() == true) ) {
			fprintf(f,"%4.1f|%4.1f|%d|%d|%s",current_temp,logic.GetTarget(),logic.heatStatus(),logic.coolStatus(),curTime);
		}
		else {  
			fprintf(f,".");
		}
		sleep(10);
		
	    if( server_running == false ){
	    	break;
	    }
	}
	pthread_exit(NULL);
}

/////////////////////////////////
////  MAIN SERVER CONTROL ///////
/////////////////////////////////
//void *mainServer(void *idp) 
//{
	/*
     * Function will be used for main controller for all thermostats
     * will contain logic for controlling the independant servers and 
     * Thermostats
	 */
//	pthread_exit(NULL);
//}

//////////////////////////////////
// MAIN //////////////////////////
//////////////////////////////////
int main(int argc, char **argv)
{
	pthread_t thread[2*ROOMS];
	pthread_attr_t attr;
	
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&threshold_cv, NULL);

/// Open and output file for verification	
	f = fopen("output.txt", "a+"); 
	if (f == 0){
		printf("error opening output.txt");
	}else{
	//	for(int i = 0; i < ROOMS; i++){
			logic.GetTime(srvrTime);
			fprintf(f,"%3.1f - %22s",logic.GetTemp(), srvrTime );
			printf("out file opened\n");
	//	}
	}
     
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    	    	
    pthread_create(&thread[0], &attr, server    , (void *)&TID[0]);    
    pthread_create(&thread[1], &attr, Thermostat, (void *)&TID[0]);   // MAIN ROOM
//    pthread_create(&thread[2], &attr, server, (void *)&TID[1]);    
//    pthread_create(&thread[3], &attr, Thermostat, (void *)&TID[1]);  // SECOND ROOM
//    pthread_create(&thread[4], &attr, server, (void *)&TID[2]);    
//    pthread_create(&thread[5], &attr, Thermostat, (void *)&TID[2]);  // BEDROOM 
//    pthread_create(&thread[6], &attr, server, (void *)&TID[3]);    
//    pthread_create(&thread[7], &attr, Thermostat, (void *)&TID[3]);  // STUDY

    printf("Leaving pthreads\n");
    pthread_join(thread[0],NULL);
    pthread_join(thread[1],NULL);
    
    pthread_exit(NULL); //just in case
   
/// close the output.txt file
	close(1);
    printf("Closing Output.txt");	
	return 0;
	
}//// END MAIN ////////////////






